/**
 * OS Module
 * Features:
 * - Provides system information (uptime, memory, disk, CPU ID).
 * - Executes shell commands.
 * - Manages device modes and rebooting.
 * 
 * Usage:
 * - Accessing OS-level and hardware-specific information and controls.
 */
import { dxos } from './libvbar-m-dxos.so'
import dxMap from './dxMap.js'
import * as std from 'std';

const dxOsObj = {};

/**
 * Get the running time of system startup (in seconds).
 * @returns {number} Running time.
 */
dxOsObj.getUptime = function () {
    return dxos.getUptime();
}

/**
 * Get the total memory of the system (in bytes).
 * @returns {number} Total memory.
 */
dxOsObj.getTotalmem = function () {
    return dxos.getTotalmem();
}

/**
 * Retrieve the remaining memory of the system (in bytes).
 * @returns {number} Remaining memory.
 */
dxOsObj.getFreemem = function () {
    return dxos.getFreemem();
}

/**
 * Get the total number of available disks in the system (in bytes).
 * @param {string} [path="/"] - The disk partition name (e.g., "/").
 * @returns {number} Total disk space.
 */
dxOsObj.getTotaldisk = function (path) {
    return dxos.getTotaldisk(path || "/");
}

/**
 * Retrieve the remaining available amount of system disk (in bytes).
 * @param {string} [path="/"] - The disk partition name (e.g., "/").
 * @returns {number} Free disk space.
 */
dxOsObj.getFreedisk = function (path) {
    return dxos.getFreedisk(path || "/");
}

/**
 * Get CPU ID.
 * @returns {string} CPU ID.
 */
dxOsObj.getCpuid = function () {
    // The C function expects a buffer size. 33 is a safe length for the CPU ID.
    return dxos.getCpuid(33);
}

/**
 * Get device UUID.
 * @returns {string} Device UUID.
 */
dxOsObj.getUuid = function () {
    // The C function expects a buffer size. 19 is a safe length for the UUID.
    return dxos.getUuid(19);
}

/**
 * Get the device's serial number (SN).
 * It first tries to read from '/etc/.sn'. If that fails, it falls back to the device UUID.
 * This operation is now handled in C layer to hide file paths.
 * @returns {string} The device's serial number.
 */
dxOsObj.getSn = function () {
    return dxos.getSn();
}

/**
 * Obtain the MAC address calculated through UUID, which can be used to initialize the network card.
 * @returns {string} MAC address in the format 'b2:a1:63:3f:99:b6'.
 */
dxOsObj.getUuid2mac = function () {
    // The C function expects a buffer size. 19 is a safe length.
    return dxos.getUuid2mac(19);
}

/**
 * Get CPU usage rate.
 * @returns {number} A number not greater than 100.
 */
dxOsObj.getFreecpu = function () {
    return dxos.getFreecpu();
}

/**
 * Execute a shell command without printing the result to the terminal.
 * @param {string} cmd - The command to execute.
 * @returns {number} The exit code of the command.
 */
dxOsObj.system = function (cmd) {
    return dxos.system(cmd);
}

/**
 * Execute a shell command and print the result to the terminal.
 * @param {string} cmd - The command to execute.
 * @returns {number} The exit code of the command.
 */
dxOsObj.systemBrief = function (cmd) {
    return dxos.systemBrief(cmd);
}

/**
 * Execute a shell command and return the result as a string.
 * @param {string} cmd - The command to execute.
 * @param {number} resLen - The maximum length of the result to receive.
 * @returns {string} The command's stdout result.
 */
dxOsObj.systemWithRes = function (cmd, resLen) {
    return dxos.systemWithRes(cmd, resLen);
}

/**
 * Execute a shell command and wait for it to complete.
 * @param {string} cmd - The command to execute.
 * @returns {number} The exit code of the command.
 */
dxOsObj.systemBlocked = function (cmd) {
    return dxos.systemBlocked(cmd);
}

/**
 * Asynchronously restart the device after a delay.
 * @param {number} delay_s - The delay in seconds before restarting.
 * @returns {number} Result of the operation.
 */
dxOsObj.asyncReboot = function (delay_s) {
    return dxos.asyncReboot(delay_s);
}

/**
 * Switch device mode. The device will restart after switching.
 * @param {string} mode - The target mode. Must be one of `"dev"`, `"test"`, `"prod"`, `"safe"`.
 * @returns {boolean} `true` if the mode switch was initiated, `false` otherwise.
 */
dxOsObj.setMode = function (mode) {
    return dxos.setMode(mode);
}

/**
 * Query current device mode.
 * @returns {string|null}
 *   - `string`: The current mode name (Must be one of `"dev"`, `"test"`, `"prod"`, `"safe"`).
 *   - `null`: If the mode file is not found or is empty.
 */
dxOsObj.getMode = function () {
    return dxos.getMode();
}

/**
 * Get the content of the OS information file and parse it as JSON.
 * @returns {object} The parsed JSON object from the file, or an empty object `{}` if the file is missing or empty.
 * @throws {SyntaxError} If the file content is not valid JSON, `JSON.parse()` will throw an exception.
 */
dxOsObj.getOsInfo = function () {
    const infoStr = dxos.getOsInfo();
    if (!infoStr||infoStr.trim() === "") { // Handles null or empty string
        return {};
    }
    return JSON.parse(infoStr);
}

/**
 * Get the content of the application project file ('/app/code/src/app.dxproj') and parse it as JSON.
 * @returns {object} The parsed JSON object from the file, or an empty object `{}` if the file is missing or empty.
 * @throws {SyntaxError} If the file content is not valid JSON, `JSON.parse()` will throw an exception.
 */
dxOsObj.getAppInfo = function () {
    const infoStr = dxos.getAppInfo();
    if (!infoStr||infoStr.trim() === "") { // Handles null or empty string
        return {};
    }
    return JSON.parse(infoStr);
}

/**
 * @namespace dxOsObj.sync
 * @description Provides a simple mechanism for synchronous-like communication between asynchronous parts of an application.
 * Note: This is a simplified implementation and may not be suitable for complex scenarios.
 */
dxOsObj.sync = {
    /**
     * Waits for a response on a specific topic.
     * @param {string} topic - The topic to wait for.
     * @param {number} timeout - Timeout in milliseconds.
     * @returns {*} The data received on the topic, or undefined if it times out.
     */
    request: function (topic, timeout) {
        let map = dxMap.get("SYNC");
        map.put(topic + "__request__", topic);
        let count = 0;
        let data;
        while (count * 10 < timeout) {
            data = map.get(topic);
            if (data !== undefined && data !== null) {
                break;
            }
            std.sleep(10);
            count += 1;
        }
        let res = map.get(topic);
        map.del(topic);
        map.del(topic + "__request__");
        return res;
    },
    /**
     * Sends a response to a specific topic.
     * @param {string} topic - The topic to respond to.
     * @param {*} data - The data to send.
     */
    response: function (topic, data) {
        let map = dxMap.get("SYNC");
        if (map.get(topic + "__request__") == topic) {
            map.put(topic, data);
        }
    },
};

/**
 * @private
 * @description A generic handler for managing component instance pointers by ID.
 * This is used internally by other DejaOS modules.
 */
dxOsObj.handleId = function (name, id, pointer) {
    // Component name cannot be empty
    if (name === undefined || name === null || name === "" || typeof name !== 'string') {
        return
    }
    let map = dxMap.get('handleIds')
    // Handle ID
    if (id === undefined || id === null || id === "" || typeof id !== 'string') {
        id = "__" + name + "_default"
    }
    if (pointer === undefined || pointer === null || typeof pointer !== 'number') {
        // If the pointer is empty, it is obtained
        return map.get(id)
    } else {
        // If the pointer is not empty, it is set
        let isExist = map.get(id)
        if (isExist) {
            // Handle already exists
            return
        }
        map.put(id, pointer)
    }
}

export default dxOsObj;